﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Text;
using System.Globalization;

namespace BlueLotus7.Web
{
    public class CalendarViewControl : BaseCalendarViewControl
    {
        #region Properties

        public bool DisplayToolbar
        {
            get;
            set;
        }

        public bool DisplayDaysOutOfMonth
        {
            get;
            set;
        }

        public List<CalendarEvent> Events
        {
            get;
            protected set;
        }

        public int? SummaryMaxLength
        {
            get;
            set;
        }

        #endregion

        #region Constructor

        public CalendarViewControl(CalendarFunctionMode mode, DayOfWeek firstdayOfWeek, int year, int month)
            : base(mode, firstdayOfWeek, year, month)
        {
            this.DisplayToolbar = true;
            this.Events = new List<CalendarEvent>();
        }

        public CalendarViewControl(CalendarFunctionMode mode, DayOfWeek firstdayOfWeek, DateTime datetime)
            : this(mode, firstdayOfWeek, datetime.Year, datetime.Month)
        {
        }

        public CalendarViewControl(CalendarFunctionMode mode)
            : this(mode, DayOfWeek.Sunday, DateTime.Now)
        {
        }

        public CalendarViewControl()
            : this(CalendarFunctionMode.Pure)
        {
        }

        #endregion

        #region Public Methods

        public override string Render()
        {
            if (this.SummaryMaxLength != null && this.SummaryMaxLength.Value < 1)
            {
                this.SummaryMaxLength = null;
            }

            StringBuilder htmlBuilder = new StringBuilder();

            htmlBuilder.Append("<div class=\"calendar " + this.Mode.ToString().ToLowerInvariant() + "\">");
            if (DisplayToolbar)
            {
                FillToolbar(htmlBuilder, this.Year, this.Month);
            }
            FillMonth(htmlBuilder, this.Year, this.Month, this.Events);
            htmlBuilder.Append("</div>");

            return htmlBuilder.ToString();
        }

        #endregion

        #region protected Methods

        protected virtual void FillToolbar(StringBuilder htmlBuilder, int currentYear, int currentMonth)
        {
            htmlBuilder.Append("<div class=\"toolbar\">");
            string functionName = this.Mode == CalendarFunctionMode.EventInHover ? "viewCalendarAjax" : "viewCalendar";

            htmlBuilder.Append("<div class=\"prev-month\" onclick=\"javascript:calendarUtil." + functionName + "($(this)," + (currentMonth < 2 ? currentYear - 1 : currentYear).ToString() + "," + (currentMonth < 2 ? 12 : currentMonth - 1).ToString() + ",'" + (this.BaseUrl == null ? string.Empty : this.BaseUrl) + "')\">&lt;&lt;</div>");
            htmlBuilder.Append("<div class=\"next-month\" onclick=\"javascript:calendarUtil." + functionName + "($(this)," + (currentMonth > 11 ? currentYear + 1 : currentYear).ToString() + "," + (currentMonth > 11 ? 1 : currentMonth + 1).ToString() + ",'" + (this.BaseUrl == null ? string.Empty : this.BaseUrl) + "')\">&gt;&gt;</div>");
            htmlBuilder.Append("<div class=\"current-month\">");
            htmlBuilder.Append("<span class=\"year\" value=\"" + currentYear.ToString() + "\">" + currentYear + "</span>");
            htmlBuilder.Append("<span class=\"month\"  value=\"" + currentMonth.ToString() + "\">" + currentMonth + "</span>");
            htmlBuilder.Append("</div>");
            htmlBuilder.Append("</div>");
        }

        /// <summary>
        /// Fills week header
        /// </summary>
        /// <param name="htmlBuilder">html builder</param>
        protected virtual void FillWeekHeader(StringBuilder htmlBuilder)
        {
            int behindDays = (int)this.FirstdayOfWeek;
            htmlBuilder.Append("<thead><tr>");

            for (int i = (int)this.FirstdayOfWeek; i <= (int)DayOfWeek.Saturday; i++)
            {
                string title = ((DayOfWeek)i).ToString();
                htmlBuilder.Append("<th class=\"" + title.ToLowerInvariant() + "\">");
                htmlBuilder.Append(this.UseShortHead ? title.Substring(0, 3) : title);
                htmlBuilder.Append("</th>");
            }

            for (int i = 0; i < behindDays; i++)
            {
                string title = ((DayOfWeek)i).ToString();
                htmlBuilder.Append("<th class=\"" + title.ToLowerInvariant() + "\">");
                htmlBuilder.Append(this.UseShortHead ? title.Substring(0, 3) : title);
                htmlBuilder.Append("</th>");
            }

            htmlBuilder.Append("</tr></thead>");
        }

        protected virtual void FillMonth(StringBuilder htmlBuilder, int year, int month, List<CalendarEvent> events)
        {
            htmlBuilder.Append("<table id=\"month" + year.ToString() + "-" + month.ToString("00") + "\" class=\"" + this.Mode.ToString().ToLowerInvariant() + "\" >");

            DateTime firstdayOfMonth = new DateTime(year, month, 1);
            int daysBeforeMonth = (7 + (int)firstdayOfMonth.DayOfWeek - (int)FirstdayOfWeek) % 7;
            FillWeekHeader(htmlBuilder);
            FillWeeks(htmlBuilder, firstdayOfMonth.AddDays(-1 * daysBeforeMonth), events);

            htmlBuilder.Append("</table>");
        }

        /// <summary>
        /// Fills week days
        /// </summary>
        /// <param name="htmlBuilder">Html builder</param>
        /// <param name="firstdayOfMonth">First day of the month</param>
        /// <param name="events">Events list</param>
        protected virtual void FillWeeks(StringBuilder htmlBuilder, DateTime firstdayOfMonth, List<CalendarEvent> events)
        {
            htmlBuilder.Append("<tbody>");
            DateTime day = firstdayOfMonth;
            while (day.AddMonths(-1).Month != this.Month)
            {
                FillWeek(htmlBuilder, ref day, events);
            }

            htmlBuilder.Append("</tbody>");
        }

        /// <summary>
        /// Fills week days
        /// </summary>
        /// <param name="htmlBuilder">Html builder</param>
        /// <param name="firstdayOfWeek">First day of the week</param>
        /// <param name="events">Events list</param>
        protected virtual void FillWeek(StringBuilder htmlBuilder, ref DateTime firstdayOfWeek, List<CalendarEvent> events)
        {
            htmlBuilder.Append("<tr class=\"week\">");
            DateTime day = firstdayOfWeek;

            for (int i = 0; i < 7; i++)
            {
                List<CalendarEvent> dayEvents = new List<CalendarEvent>();
                FillDay(htmlBuilder, ref day, FilterEventByDay(day, events));
            }

            htmlBuilder.Append("</tr>");
            firstdayOfWeek = firstdayOfWeek.AddDays(7);
        }

        protected virtual void FillDay(StringBuilder htmlBuilder, ref DateTime day, List<CalendarEvent> events)
        {
            htmlBuilder.Append("<td class=\"" + day.DayOfWeek.ToString().ToLowerInvariant() + " " + (this.Month == day.Month ? "in" : "out") + "\" value=\"" + day.ToString((string.IsNullOrWhiteSpace(this.DateFormat) ? defaultDateFormat : this.DateFormat), CultureInfo.InvariantCulture) + "\">");
            if (this.DisplayDaysOutOfMonth || this.Month == day.Month)
            {
                htmlBuilder.Append("<div class=\"day " + ((events.Count > 0 && this.Mode == CalendarFunctionMode.EventInHover) ? "has-event" : string.Empty) + "\" tt=\"" + (this.Mode == CalendarFunctionMode.EventInHover ? GenerateDayEventSummary(events) : string.Empty) + "\">");
                FillDayBasicInfo(htmlBuilder, day);
                if (this.Mode == CalendarFunctionMode.EventSummary)
                {
                    FillDayAdvancedInfo(htmlBuilder, events);
                }
                htmlBuilder.Append("</div>");
            }
            htmlBuilder.Append("</td>");
            day = day.AddDays(1);
        }

        protected virtual void FillDayBasicInfo(StringBuilder htmlBuilder, DateTime day)
        {
            htmlBuilder.Append("<div class=\"basic\">");
            if (!string.IsNullOrWhiteSpace(BaseRedirectUrl))
            {
                BaseRedirectUrl = BaseRedirectUrl + ((BaseRedirectUrl.IndexOf('?') < 0) ? "?" : "&");
                htmlBuilder.Append("<a href=\"" + (BaseRedirectUrl + "year=" + day.Year.ToString() + "&month=" + day.Month.ToString()) + "\" >");
                htmlBuilder.Append(day.Day.ToString());
                htmlBuilder.Append("</a>");
            }
            else
            {
                htmlBuilder.Append(day.Day.ToString());
            }
            htmlBuilder.Append("</div>");
        }

        protected virtual void FillDayAdvancedInfo(StringBuilder htmlBuilder, List<CalendarEvent> events)
        {
            htmlBuilder.Append("<div class=\"advanced\">");

            htmlBuilder.Append("<ul>");
            foreach (CalendarEvent e in events)
            {
                if (e.Summary == null)
                {
                    e.Summary = string.Empty;
                }
                if (e.Identity == null)
                {
                    e.Identity = string.Empty;
                }
                htmlBuilder.Append("<li id=\"eid_" + e.Identity + "\" class=\"" + e.Type.ToString().ToLowerInvariant() + "\"><a href=\"javascript:calendarUtil.viewEvent('" + e.Identity + "')\">" + ((this.SummaryMaxLength != null && e.Summary.Length > this.SummaryMaxLength.Value) ? e.Summary.Substring(0, this.SummaryMaxLength.Value) + "..." : e.Summary) + "</a></li>");
            }

            htmlBuilder.Append("</ul>");
            htmlBuilder.Append("</div>");
        }

        /// <summary>
        /// Filters events according to the date scope.
        /// Events which are in date scope will be removed in source collection.
        /// </summary>
        /// <param name="startStamp">start datetime</param>
        /// <param name="endStamp">end datetime</param>
        /// <param name="events">event list</param>
        /// <returns>Events matched in date scope.</returns>
        protected virtual List<CalendarEvent> FilterEvent(DateTime startStamp, DateTime endStamp, List<CalendarEvent> events)
        {
            List<CalendarEvent> result = new List<CalendarEvent>();

            if (events != null)
            {
                for (int i = 0; i < events.Count; i++)
                {
                    CalendarEvent e = events[i];
                    if (e.startStamp >= startStamp && e.endStamp < endStamp)
                    {
                        result.Add(e);
                        events.RemoveAt(i);
                        i--;
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Filters events according to the date scope.
        /// Events which are in date scope will be removed in source collection.
        /// </summary>
        /// <param name="startStamp">start datetime</param>
        /// <param name="endStamp">end datetime</param>
        /// <param name="events">event list</param>
        /// <returns>Events matched in date scope.</returns>
        protected virtual List<CalendarEvent> FilterEventByDay(DateTime stamp, List<CalendarEvent> events)
        {
            DateTime startTime, endTime;
            startTime = endTime = new DateTime(stamp.Year, stamp.Month, stamp.Day, 0, 0, 0);
            endTime = endTime.AddDays(1);
            List<CalendarEvent> result = new List<CalendarEvent>();

            if (events != null)
            {
                for (int i = 0; i < events.Count; i++)
                {
                    CalendarEvent e = events[i];
                    if (e.startStamp >= startTime && e.endStamp < endTime)
                    {
                        result.Add(e);
                        events.RemoveAt(i);
                        i--;
                    }
                }
            }

            return result;
        }

        /// <summary>
        /// Generates day event summary
        /// </summary>
        /// <param name="events">event list.</param>
        /// <returns>event summary string</returns>
        protected virtual string GenerateDayEventSummary(List<CalendarEvent> events)
        {
            StringBuilder builder = new StringBuilder();

            int i = 1;
            foreach (CalendarEvent e in events)
            {
                builder.Append("[" + i.ToString() + "] " + HttpUtility.HtmlEncode(e.Summary) + "<br />");
                i++;
            }

            return System.Web.HttpUtility.HtmlEncode(builder.ToString());
        }

        #endregion
    }
}